package com.atguigu.gmall.activity.receiver;

import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.service.SeckillGoodsService;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.model.activity.SeckillGoods;
import com.atguigu.gmall.model.activity.UserRecode;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rabbitmq.client.Channel;
import lombok.SneakyThrows;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;

/**
 * @author mqx
 * @date 2020-11-25 09:30:01
 */
@Component
public class SeckillReceiver {

    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;

    @Autowired
    private SeckillGoodsService seckillGoodsService;

    @Autowired
    private RedisTemplate redisTemplate;

    //  监听消息
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_TASK_1,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK),
            key = {MqConst.ROUTING_TASK_1}
    ))
    public void importItemToRedis(Message message, Channel channel){
        /*
        1.  先查询到所有的秒杀商品
            审核状态为1 ，库存商量大于0,开始时间是今天
        2.  将商品放入缓存
        3.  商品的库存数量放入缓存
        4.  更新状态位：
                状态位：1 表示有库存，状态位是 0 表示没有库存！
         */
        QueryWrapper<SeckillGoods> seckillGoodsQueryWrapper = new QueryWrapper<>();
        seckillGoodsQueryWrapper.eq("status","1");
        seckillGoodsQueryWrapper.gt("stock_count",0);
        //  时间比较：只需要比较年月日！ 时间比较工具类，在购物车用过！
        //  date_format(start_time,'%Y-%m-%d') mysql 中的日期函数
        seckillGoodsQueryWrapper.eq("DATE_FORMAT(start_time,'%Y-%m-%d')", DateUtil.formatDate(new Date()));
        //  查询到的所有秒杀商品！
        List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.selectList(seckillGoodsQueryWrapper);
        //  31,32,33
        //  判断
        if (!CollectionUtils.isEmpty(seckillGoodsList)){
            //  循环遍历
            for (SeckillGoods seckillGoods : seckillGoodsList) {
                //  需要做个判断：当前缓存中是否已经有了该秒杀商品，如果有，不放入！
                Boolean flag = redisTemplate.boundHashOps(RedisConst.SECKILL_GOODS).hasKey(seckillGoods.getSkuId().toString());
                //  如果flag = true; 有key
                if (flag){
                    //  结束本次循环，继续下次循环！
                    continue;
                }
                //  需要将每个秒杀的商品都放入缓存！    hash：hset(key,field,value);
                //  key = seckill:goods, field = skuId  value = 整个seckillGoods
                redisTemplate.boundHashOps(RedisConst.SECKILL_GOODS).put(seckillGoods.getSkuId().toString(),seckillGoods);

                //  将商品的库存数量放入缓存：如何防止库存超卖，利用redis 的特性 将数据保存到list中。
                //  fori 普通循环 ， for 增强for
                for (Integer i = 0; i < seckillGoods.getStockCount(); i++) {
                    //  定义key ： seckill:stock:skuId value = skuId
                    redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX+seckillGoods.getSkuId().toString()).leftPush(seckillGoods.getSkuId().toString());
                }

                //  数据初始化的时候，应该发送消息 publish seckillpush 31:1 ,每个是商品都可以秒杀！
                redisTemplate.convertAndSend("seckillpush",seckillGoods.getSkuId()+":1");


            }
        }

        //  消息确认
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

    //  需要监听消息队列数据
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_SECKILL_USER,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_SECKILL_USER),
            key = {MqConst.ROUTING_SECKILL_USER}
    ))
    public void seckill(UserRecode userRecode,Message message,Channel channel){
        if (userRecode!=null){
            //  预下单+减库存
            seckillGoodsService.seckillOrder(userRecode.getUserId(),userRecode.getSkuId());
            //  确认消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }

    }

    //  监听消息
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_TASK_18,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK),
            key = {MqConst.ROUTING_TASK_18}
    ))
    public void clearRedis(Message message, Channel channel){
        //  只需要删除缓存中的数据。
        //  秒杀商品的key seckill:goods
        //  商品的库存数 seckill:stock:31
        //  哪个用户的秒杀：seckill:user:1
        //  秒杀订单：seckill:orders:users
        //  根据这两个条件查询哪些商品结束了秒杀 ：status = 1 ，end_time
        QueryWrapper<SeckillGoods> seckillGoodsQueryWrapper = new QueryWrapper<>();
        seckillGoodsQueryWrapper.eq("status",1);
        seckillGoodsQueryWrapper.eq("DATE_FORMAT(end_time,'%Y-%m-%d')", DateUtil.formatDate(new Date()));
        //  查询到的集合
        List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.selectList(seckillGoodsQueryWrapper);
        //  循环遍历
        for (SeckillGoods seckillGoods : seckillGoodsList) {
            //  删除秒杀库存
            redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX +seckillGoods.getSkuId().toString());
            //  删除秒杀商品
            redisTemplate.boundHashOps(RedisConst.SECKILL_GOODS).delete(seckillGoods.getSkuId().toString());
        }
        //  要想删除 seckill:user:1 ，必须要获取到用户Id。在发送消息的时候将用户Id 传递过来，
        //  redisTemplate.delete()
        //  删除秒杀订单的。
        redisTemplate.delete(RedisConst.SECKILL_ORDERS_USERS);
        //  删除预下单的数据，用户抢购成功，不下单,则需要将用户的预下单数据删除。
        redisTemplate.delete(RedisConst.SECKILL_ORDERS);

        //  处理完缓存之后，将数据库的秒杀商品数据，进行更改状态status 。
        SeckillGoods seckillGoods = new SeckillGoods();
        seckillGoods.setStatus("2");
        seckillGoodsMapper.update(seckillGoods,seckillGoodsQueryWrapper);

        //  消息确认
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

}
